package com.kehutong.charge.service.impl;

import com.alibaba.excel.util.FileUtils;
import com.kehutong.charge.dao.ChargeArrearsDao;
import com.kehutong.charge.dto.ChargeStatistDTO;
import com.kehutong.charge.entity.ChargeArrears;
import com.kehutong.charge.entity.ChargeArrearsConfig;
import com.kehutong.charge.entity.ChargeBillItems;
import com.kehutong.charge.entity.ChargeUrgingCover;
import com.kehutong.charge.service.ChargeArrearsService;
import com.kehutong.charge.util.CommonClient;
import com.kehutong.charge.util.ConvertUtil;
import com.kehutong.charge.util.ImageUtil;
import com.kehutong.charge.util.enums.ArrearsSetTypeEnum;
import com.kehutong.charge.util.enums.ChargeBillStatusEnum;
import com.kehutong.charge.vo.*;
import com.kehutong.common.DailyUtil;
import com.kehutong.common.DeptService;
import com.kehutong.common.DoveClient;
import com.kehutong.common.PageUtils;
import com.kehutong.common.constant.CommonConstant;
import com.kehutong.common.dto.DeptDTO;
import com.kehutong.common.enums.TemplateType;
import com.kehutong.common.errcode.Assert;
import com.kehutong.common.errcode.Result;
import com.kehutong.common.excel.tool.ExcelTool;
import com.kehutong.common.urlconfig.FileUrlConfig;
import com.kehutong.common.util.*;
import org.coraframework.converter.util.StringUtil;
import org.coraframework.inject.Inject;
import org.coraframework.json.JSONArray;
import org.coraframework.json.JSONObject;
import org.coraframework.logger.Logger;
import org.coraframework.logger.LoggerFactory;
import org.coraframework.mvc.webservice.WebService;
import org.coraframework.orm.Pool;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.orm.jdbc.Page;
import org.coraframework.orm.jdbc.execute.Condition;
import org.coraframework.orm.jdbc.execute.PageCondition;
import org.coraframework.orm.jdbc.execute.QueryCondition;
import org.coraframework.orm.jdbc.execute.entity.FindArray;
import org.coraframework.orm.jdbc.execute.entity.FindPage;
import org.coraframework.util.Objects;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @program: kht
 * @description: ChargeArrearServiceImpl
 * @author: joneron
 * @create: 2021/7/2
 **/
@WebService("/charge/arrears/service")
public class ChargeArrearsServiceImpl implements ChargeArrearsService {

    private static final Logger logger = LoggerFactory.getLogger(ChargeArrearsServiceImpl.class);
    private static final long MEDIA_ID_EXPIRES = 3*24*60*60-100;
    private static final String IMAGES_URL = "https://kf.khtong.cn/pic/charge.jpg";

    @Inject
    private JdbcSession jdbcSession;
    @Inject
    private ChargeArrearsDao chargeArrearsDao;
    @Inject
    private ExcelTool excelTool;
    @Inject
    DeptService deptService;
    @Inject
    private CommonClient commonClient;
    @Inject
    private DoveClient doveClient;
    @Inject
    private FileUrlConfig fileUrlConfig;





    @Override
    public Object configSave(ChargeArrearsConfig entity, Token token) throws Exception {

        if(StringUtil.isEmpty(entity.getCommunityName())){
            return Result.error(0,"添加失败！请选择需要添加的小区.");
        }
        if(entity.getCommunityName().equals(CommonConstant.DEFAULT_COMMUNITY_NAME)){
            entity.setInitData(true);
            entity.setCommunityNo(CommonConstant.DEFAULT_COMMUNITY_NO);
            entity.setSort(999);
            entity.setHide(false);
        }

        ChargeArrearsConfig arrearsConfig = jdbcSession.findOne(ChargeArrearsConfig.class)
                .eq("deleted", false)
                .eq("communityNo", entity.getCommunityNo())
                .exe();
        if(!Objects.isNull(arrearsConfig)){
            return Result.error(0,"添加失败！一个项目（小区）仅能有一个欠费配置.");
        }
        List<String> departList = token.getDepartments();
        entity.setCreateName(token.getUsername());
        if(!Objects.isEmpty(departList)){
            DeptDTO deptDTO = deptService.getDept(departList.get(0));
            entity.setDepartmentNo(deptDTO.getDepartmentNo());
            entity.setDepartmentNos(deptDTO.getDepartmentNos());
            entity.setDepartmentName(deptDTO.getDepartmentName());
        }
        return jdbcSession.insert(entity)==1;
    }


    private void initDefaultConfigCreate() {
        ChargeArrearsConfig dbGlobalConfig = jdbcSession.findOne(ChargeArrearsConfig.class)
                //.eq("deleted", false) 需求变更，可删除，但是不能二次初始化
                .eq("communityNo", CommonConstant.DEFAULT_COMMUNITY_NO)
                .exe();
        if(null != dbGlobalConfig){ return; }
        ChargeArrearsConfig globalConfig = Pool.newInstance(ChargeArrearsConfig.class);
        globalConfig.setCommunityNo(CommonConstant.DEFAULT_COMMUNITY_NO);
        globalConfig.setCommunityName(CommonConstant.DEFAULT_COMMUNITY_NAME);
        globalConfig.setArrearsSetTypeEnum(ArrearsSetTypeEnum.PAST_AND_PRESENT);
        globalConfig.setCreateName("System");
        globalConfig.setSort(999);
        globalConfig.setHide(true);
        globalConfig.setDeleted(false);
        globalConfig.setInitData(true);
        jdbcSession.insert(globalConfig);
    }


    @Override
    public Object configUpdate(ChargeArrearsConfig entity) {
        if(null == entity || StringUtil.isEmpty(entity.getId())){
            return Result.error(0,"需要编辑的数据id不能为空.");
        }
        if(entity.isInitData() && !Objects.isEmpty(entity.getCommunityNo())){
            throw new RuntimeException("默认初始化数据不能修改");
        }
        return jdbcSession.updateById(entity);
    }


    @Override
    public Object configDeleteById(String id) {
        ChargeArrearsConfig result = Pool.get(ChargeArrearsConfig.class, id);
        if (result == null) {
            return Result.error(0,"需要删除的数据不存在.");
        }
        result.setDeleted(true);
        return jdbcSession.updateById(result);
    }


    @Override
    public Object configPage(JSONObject jsonObject) {
        this.initDefaultConfigCreate();

        String communityNo = jsonObject.getString("communityNo");
        FindPage<ChargeArrearsConfig> find = jdbcSession.findPage(ChargeArrearsConfig.class);
        find.eq("deleted", false);
        if(!Objects.isEmpty(communityNo)){
            find.eq("communityNo", communityNo);
        }
        find.order("sort", "desc");
        find.order("createTime", "desc");
        return find.page(jsonObject).exe();
    }


    @Override
    public Page<ChargeArrears> findPage(JSONObject jsonObject) {
        return null;
    }


    @Override
    public Page<ChargeBillItemsVO> findCommunityPage(JSONObject jsonObject) {
        String communityNo = jsonObject.getString("communityNo");
        String buildNo = jsonObject.getString("buildNo");
        String houseNo = jsonObject.getString("houseNo");
        String chargeNameOrMobile = jsonObject.getString("chargeNameOrMobile");
        String typeName = jsonObject.getString("typeName");
        String userNo = jsonObject.getString("userNo");
        JSONArray listUserNo = jsonObject.getJSONArray("listUserNo");
        String startMonth = jsonObject.getString("startMonth");
        String endMonth = jsonObject.getString("endMonth");

        //ORM
        PageCondition query = Condition.createPage(false);
        if(!StringUtil.isEmpty(typeName)){
            query.like("chargeName",typeName);
        }
        if(!StringUtil.isEmpty(userNo)){
            query.eq("userNo",userNo);
        }
        if(!Objects.isEmpty(listUserNo)){
            query.in("userNo",listUserNo);
        }
        if(!StringUtil.isEmpty(buildNo)){
            query.eq("buildNo",buildNo);
        }
        if(!StringUtil.isEmpty(houseNo)){
            query.eq("houseNo",houseNo);
        }
        if(!StringUtil.isEmpty(chargeNameOrMobile)){
            query.markBegin();
            query.like("chargeName",chargeNameOrMobile);
            query.or();
            query.eq("mobile",chargeNameOrMobile);
            query.markEnd();
        }

        if(!Objects.isEmpty(startMonth) && !Objects.isEmpty(endMonth)) {
            query.maxEq("billYearMonth", startMonth);
            query.minEq("billYearMonth", endMonth);
        } else if (!Objects.isEmpty(startMonth)) {
            query.eq("billYearMonth", startMonth);
        } else if (!Objects.isEmpty(endMonth)){
            query.min("billYearMonth", endMonth);
        } else {
            ArrearsSetTypeEnum arrearsSetTypeEnum = this.getArrearsSetTypeEnum(communityNo);
            if(ArrearsSetTypeEnum.THIS_MONTH.equals(arrearsSetTypeEnum)){
                String yearMonthNow = new SimpleDateFormat("yyyy-MM").format(Calendar.getInstance().getTime());
                query.eq("billYearMonth", yearMonthNow);
            }
        }
        if(!Objects.isEmpty(communityNo)){
            query.eq("communityNo",communityNo);
        }
        query.eq("deleted", false);
        query.eq("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
        //添加选中导出条件
        JSONArray exportIdList1 = jsonObject.getJSONArray("exportIdList1");
        JSONArray exportIdList2 = jsonObject.getJSONArray("exportIdList2");
        if(!Objects.isEmpty(exportIdList1) && !Objects.isEmpty(exportIdList2)){
            query.in("houseNo",exportIdList1);
            query.in("userNo",exportIdList2);
        }

        query.groupBy("userNo");
        query.groupBy("houseNo");
        query.order("createTime","desc");
        Page<ChargeBillItemsVO> communityPage = chargeArrearsDao.findCommunityPage(query);
        //框架bug直接使用实体映射会不定时报错
        List<ChargeBillItemsVO> mapList = communityPage.getList();
        List<ChargeBillItemsVO> supplierList = ListUtils.copyList(mapList, ChargeBillItemsVO::new);
        communityPage.setList(supplierList);

        List<ChargeBillItemsVO> list = communityPage.getList();
        Set<String> houseNos = list.stream().map(t -> t.getHouseNo()).collect(Collectors.toSet());
        Set<String> userNos = list.stream().map(ChargeBillItemsVO::getUserNo).collect(Collectors.toSet());
        try {
            JSONObject json = new JSONObject();
            json.put("ids",houseNos);
            List<Map<String, Object>> houseList = commonClient.getHouseByIds(json);
            if(!Objects.isEmpty(houseList)){
                list.stream().map(t->houseList.stream().filter(h->t.getHouseNo().equals(h.get("id"))).findFirst().map(h->{
                    String address = ConvertUtil.jointAddress(h.get("communityName").toString(), h.get("buildName").toString(),
                            h.get("unitName").toString(), h.get("mergeName").toString());
                    t.setBillAddress(address);
                    t.setAvatar((String)h.get("avatar"));
                    return t;
                }).orElse(null)).filter(Objects::nonNull).collect(Collectors.toList());
            }
            communityPage.setList(list);
        } catch (Exception e) {
            logger.warn("欠费功能获取资产房屋错误,houseNos:{},错误信息(不影响功能):",houseNos,e);
        }

        try {
            JSONObject accountResult = doveClient.post("/auth/account/service/list", (http) -> {
                JSONObject body = new JSONObject();
                body.put("id", userNos);
                http.setBody(body.toJSONString());
            });
            JSONArray data = accountResult.getJSONArray("data");
            if (Objects.nonEmpty(data)) {
                for (int i = 0; i < list.size(); i++) {
                    ChargeBillItemsVO vo = list.get(i);
                    for (int j = 0; j < data.size(); j++) {
                        JSONObject tempJson = data.getJSONObject(j);
                        String userNoTemp = tempJson.getString("id");
                        String mobileTemp = tempJson.getString("mobile");
                        if (vo.getUserNo().equals(userNoTemp) && Objects.nonEmpty(mobileTemp)) {
                            vo.setMobile(mobileTemp);
                        }
                    }
                }

            }
        } catch (Exception e) {
            logger.warn("欠费功能获取缴费人手机号错误,userNos:{},错误信息(不影响功能):{}",userNos,e);
        }

        return communityPage;
    }


    @Override
    public Object loadUrgingOrganizeTree(String communityNo) {
        if (Objects.isEmpty(communityNo)) {
            return Result.error(0, "请先选择小区");
        }

        FindArray<ChargeBillItems> findArray = jdbcSession.findArray(ChargeBillItems.class)
                                                          .eq("communityNo", communityNo)
                                                          .eq("deleted", false)
                                                          .in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());

        ArrearsSetTypeEnum arrearsSetTypeEnum = this.getArrearsSetTypeEnum(communityNo);
        if(ArrearsSetTypeEnum.THIS_MONTH.equals(arrearsSetTypeEnum)) {
            String yearMonthNow = new SimpleDateFormat("yyyy-MM").format(Calendar.getInstance().getTime());
            findArray.eq("billYearMonth", yearMonthNow);
        }

        // 查询所有欠费账单
        List<ChargeBillItems> list = findArray.groupBy("communityNo")
                                              .groupBy("buildNo")
                                              .groupBy("houseNo")
                                              .exe();

        if (Objects.isEmpty(list)) {
            return Result.error(0, "该小区暂无欠费数据");
        }

        Comparator<ChargeBillItems> byBuild = Comparator.comparing(ChargeBillItems::getBuildName);
        Comparator<ChargeBillItems> byHouse = Comparator.comparing(ChargeBillItems::getMergeName);
        list.sort(byBuild.thenComparing(byHouse));

        // 设置第一层小区信息
        ChargeBillItemsOrganizeTreeVO result = new ChargeBillItemsOrganizeTreeVO();
        result.setId(communityNo);
        result.setName(list.get(0).getCommunityName());

        Set<String> buildSet = new HashSet<>();
        for (ChargeBillItems items : list) {
            if (buildSet.contains(items.getBuildNo())) {
                continue;
            }
            // 设置第二层楼栋信息
            ChargeBillItemsOrganizeTreeVO buildTemp = new ChargeBillItemsOrganizeTreeVO();
            buildTemp.setId(items.getBuildNo());
            buildTemp.setName(items.getBuildName());
            buildTemp.setPid(communityNo);

            for (ChargeBillItems houseItems : list) {
                if (houseItems.getBuildNo()
                              .equals(items.getBuildNo()) && !buildSet.contains(items.getBuildNo())) {
                    // 设置第三层房屋信息
                    ChargeBillItemsOrganizeTreeVO houseTemp = new ChargeBillItemsOrganizeTreeVO();
                    houseTemp.setId(houseItems.getHouseNo());
                    houseTemp.setName(houseItems.getMergeName());
                    houseTemp.setPid(houseItems.getBuildNo());

                    buildTemp.addChild(houseTemp);
                }
            }
            result.addChild(buildTemp);
            buildSet.add(items.getBuildNo());
        }

        return Result.success(result);
    }


    @Override
    public Object totalUserArrears(JSONObject jsonObject) {
        String houseNo = jsonObject.getString("houseNo");
        String userNo = jsonObject.getString("userNo");

        if(Objects.isEmpty(houseNo) || Objects.isEmpty(userNo)){
            return Result.error(0,"房屋和用户编号不能为空.");
        }

        List<ChargeBillItems> billItems = jdbcSession.findArray(ChargeBillItems.class)
                .eq("deleted", false)
                .eq("houseNo", houseNo)
                .eq("userNo", userNo)
                .in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId())
                .exe();

        if(Objects.isEmpty(billItems)){
            return BigDecimalUtils.format(BigDecimal.ZERO);
        }
        BigDecimal totalAmount = billItems.stream().map(b -> b.getPayableAmount()).reduce(BigDecimal::add).get();
        logger.info("用户：{}，获取到欠费总金额为：{}",userNo,totalAmount);
        return BigDecimalUtils.format(totalAmount);
    }


    @Override
    public void pageExport(HttpServletRequest req, HttpServletResponse resp, JSONObject jsonObject) {
        jsonObject.put("pageNo", 1);
        jsonObject.put("pageSize", Short.MAX_VALUE);
        List<ChargeBillItemsVO> list = findCommunityPage(jsonObject).getList();
        Assert.notEmpty(list,"导出失败！该小区没有找到相关欠费住户数据.");
        List<ChargeArrearsExcelVO> dtoList = ListUtils.copyBeanList(list, ChargeArrearsExcelVO::new);
        excelTool.excelDownload(req,resp,dtoList,"小区欠费用户列表导出");
    }


    @Override
    public List<ChargeArrears> findList(JSONObject jsonObject) {
        return jdbcSession.findArray(ChargeArrears.class)
                .eq("deleted", false)
                .exe();
    }


    @Override
    public Object save(ChargeArrears chargeArrears) {


        return jdbcSession.insert(chargeArrears);
    }


    @Override
    public Object update(ChargeArrears chargeArrears) {
        return jdbcSession.updateById(chargeArrears);
    }


    @Override
    public Object deleteById(String id) {
        ChargeArrears result = Pool.get(ChargeArrears.class, id);
        if (result != null) {
            result.setDeleted(true);
            jdbcSession.updateById(result);
        }
        return 1;
    }


    /**
     * 小程序根据住户统计欠费分页
     * @param jsonObject
     * @param token
     * @return
     */
    @Override
    public Object residentStatistPage(JSONObject jsonObject, Token token) throws Exception {
        String search = jsonObject.getString("search");
        String chargeName = jsonObject.getString("chargeName");
        String communityNo = jsonObject.getString("communityNo");
        String buildNo = jsonObject.getString("buildNo");
        String houseNo = jsonObject.getString("houseNo");
        String feeTypeNo = jsonObject.getString("feeTypeNo");
        String billYearMonth = jsonObject.getString("billYearMonth");
        //违约时间
        String startTime = jsonObject.getString("startTime");
        String endTime = jsonObject.getString("endTime");
        String userNo = jsonObject.getString("userNo");

        PageCondition find = Condition.createPage(false);
        find.eq("deleted", false);
        find.eq("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
        if(!Objects.isEmpty(chargeName)){
            find.like("chargeName", chargeName);
        }
        if(!Objects.isEmpty(communityNo)){
            find.eq("communityNo", communityNo);
        }
        if(!Objects.isEmpty(buildNo)){
            find.eq("buildNo", buildNo);
        }
        if(!Objects.isEmpty(houseNo)){
            find.eq("houseNo", houseNo);
        }
        if(!Objects.isEmpty(feeTypeNo)){
            find.eq("feeTypeNo", feeTypeNo);
        }
        if(!Objects.isEmpty(billYearMonth)){
            find.eq("billYearMonth", billYearMonth);
        }
        if(Objects.nonEmpty(userNo)){
            find.eq("userNo", userNo);
        }
        //企微端是否好友查询
        boolean isFriend = jsonObject.getBooleanValue("isFriend");
        if(isFriend){
            //初始化封面配置


            JSONArray friendUserNos = commonClient.getFriendUserNos(token.getUuid());
            JSONArray stewardHouseNos = commonClient.getStewardHouseNos(token.getUuid());
            find.in("userNo", friendUserNos);
            find.in("houseNo", stewardHouseNos);
        }
        if(!Objects.isEmpty(startTime) && !Objects.isEmpty(endTime) ){
            find.maxEq("penaltyDate", startTime);
            find.minEq("penaltyDate", endTime);
        }
        find.groupBy("userNo");
        find.order("createTime", "desc");
        if(!Objects.isEmpty(search)){
            find.markBegin();
            find.like("communityName", search).or();
            find.like("chargeName", search).or();
            find.like("billAddress", search).or();
            find.markEnd();
        }
        find.page(jsonObject);
        Page<ChargeBillItemsVO> page = chargeArrearsDao.statistPage(find);
        if(Objects.isEmpty(page.getList())){
            return page;
        }
        try {
            //获取、填充头像
            List<ChargeBillItemsVO> list = page.getList();
            Set<String> ids = list.stream().map(l -> l.getUserNo()).collect(Collectors.toSet());
            JSONObject param = new JSONObject();
            param.put("ids",ids);
            List<Map<String,Object>> residentList = commonClient.getResidentByIds(param);
            list.stream().map(l->residentList.stream().filter(r->r.get("id").equals(l.getUserNo())).findFirst().map(r->{
                l.setAvatar((String)r.get("avatar"));
                return l;
            }).orElse(null)).filter(java.util.Objects::nonNull).collect(Collectors.toList());
        } catch (Exception e) {
            logger.error("[催费列表]获取头像异常（仅打印日志，不影响功能）",e);
        }
        return page;
    }


    /**
     * 初始化企微群发消息到小程序封面图片
     * @throws Exception
     */

    public ChargeUrgingCover initCoverImages() throws Exception {
        List<ChargeUrgingCover> cover = jdbcSession.findArray(ChargeUrgingCover.class)
                .eq("deleted", false)
                .exe();
        if(!Objects.isEmpty(cover)){
            return cover.get(0);
        }

        //下载远程图片
        logger.info("[初始化企微群发封面图片]从（{}）远程下载图片为 file",IMAGES_URL);
        File file = ImageUtil.downloadImage2File(IMAGES_URL,0);
        //上传图片到服务器获取文件id
        JSONObject jsonObject = fileUrlConfig.sendFile(file, http->{ });
        JSONObject data = jsonObject.getJSONObject("data");
        String fileId = data.getString("id");
        Long size = data.getLong("size");
        logger.info("[初始化企微群发封面图片]上传图片到服务器获取文件id:{},size:{}",fileId,size);
        //上传图片到素材库获取图片ID
        String imageId = commonClient.uploadImage2Material(fileId, size);
        ChargeUrgingCover urgingCover = Pool.newInstance(ChargeUrgingCover.class);
        urgingCover.setImageId(imageId);
        urgingCover.setCreateName("System");
        jdbcSession.insert(urgingCover);
        return urgingCover;
    }



    /**
     * 小程序根据用户获取欠费详情和该用户欠费列表
     * @param jsonObject
     * @return
     */
    @Override
    public Object residentDetailsList(JSONObject jsonObject) {
        String userNo = jsonObject.getString("userNo");
        String communityNo = jsonObject.getString("communityNo");
        String buildNo = jsonObject.getString("buildNo");
        String houseNo = jsonObject.getString("houseNo");
        String feeTypeNo = jsonObject.getString("feeTypeNo");
        String billYearMonth = jsonObject.getString("billYearMonth");
        //违约时间
        String startTime = jsonObject.getString("startTime");
        String endTime = jsonObject.getString("endTime");


        QueryCondition find = Condition.createQuery(false);
        find.eq("deleted", false);
        find.in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
        find.eq("userNo", userNo);


        if(!Objects.isEmpty(communityNo)){
            find.eq("communityNo", communityNo);
        }
        if(!Objects.isEmpty(buildNo)){
            find.eq("buildNo", buildNo);
        }
        if(!Objects.isEmpty(houseNo)){
            find.eq("houseNo", houseNo);
        }
        if(!Objects.isEmpty(feeTypeNo)){
            find.eq("feeTypeNo", feeTypeNo);
        }
        if(!Objects.isEmpty(billYearMonth)){
            find.eq("billYearMonth", billYearMonth);
        }
        if(!Objects.isEmpty(startTime) && !Objects.isEmpty(endTime) ){
            find.maxEq("penaltyDate", startTime);
            find.minEq("penaltyDate", endTime);
        }
        find.groupBy("billYearMonth");
        find.order("billYearMonth");
        List<ChargeBillItemsVO> list = chargeArrearsDao.getArrearsByUserNo(find);
        BigDecimal allAmountTotal = list.stream().map(l -> l.getAmountTotal()).reduce(BigDecimal::add).get();

        Map<String,Object> result = new HashMap<>(3);
        result.put("list",list);
        result.put("allAmountTotal",BigDecimalUtils.format(allAmountTotal));

        return result;
    }


    /**
     * 小程序根据住户获取账单列表
     * @param jsonObject
     * @return
     */
    @Override
    public Object itemDetailsList(JSONObject jsonObject) {
        String userNo = jsonObject.getString("userNo");
        String communityNo = jsonObject.getString("communityNo");
        String buildNo = jsonObject.getString("buildNo");
        String houseNo = jsonObject.getString("houseNo");
        String feeTypeNo = jsonObject.getString("feeTypeNo");
        String billYearMonth = jsonObject.getString("billYearMonth");
        //违约时间
        String startTime = jsonObject.getString("startTime");
        String endTime = jsonObject.getString("endTime");


        FindArray<ChargeBillItems> find = jdbcSession.findArray(ChargeBillItems.class);
        find.eq("deleted", false);
        find.in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
        find.like("userNo", userNo);
        find.eq("billYearMonth", billYearMonth);

        if(!Objects.isEmpty(communityNo)){
            find.eq("communityNo", communityNo);
        }
        if(!Objects.isEmpty(buildNo)){
            find.eq("buildNo", buildNo);
        }
        if(!Objects.isEmpty(houseNo)){
            find.eq("houseNo", houseNo);
        }
        if(!Objects.isEmpty(feeTypeNo)){
            find.eq("feeTypeNo", feeTypeNo);
        }
        if(!Objects.isEmpty(startTime) && !Objects.isEmpty(endTime) ){
            find.maxEq("penaltyDate", startTime);
            find.minEq("penaltyDate", endTime);
        }
        find.order("createTime", "desc");
        return find.exe();
    }


    /**
     * 根据小区统计欠费分页
     * @param jsonObject
     * @return
     */
    @Override
    public Object statistPage(JSONObject jsonObject){
        this.initDefaultConfigCreate();
        //1、获取所有欠费小区分页数据
        int count;
        List<String> communityNos = new LinkedList<>();
        String communityNo = jsonObject.getString("communityNo");
        if (!Objects.isEmpty(communityNo)) {
            communityNos.add(communityNo);
            count = 1;
        }else {
            PageCondition find = Condition.createPage(false);
            find.eq("deleted", false);
            find.in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
            find.groupBy("communityNo");
            find.order("createTime", "desc");
            Page<ChargeBillItems> page = chargeArrearsDao.arrearsItemCommunityPage(find);

            List<com.kehutong.charge.entity.ChargeBillItems> list = page.getList();
            if(Objects.isEmpty(list)){
                return PageUtils.buildPage(jsonObject, new ArrayList<>());
            }
            Set<String> pageCommunityNos = list.stream().map(l -> l.getCommunityNo()).collect(Collectors.toSet());
            communityNos.addAll(pageCommunityNos);
            count = page.getCount();
        }

        //2、查询出所有缴费配置信息
        List<ChargeArrearsConfig> allConfigs = jdbcSession.findArray(ChargeArrearsConfig.class)
                .eq("deleted", false)
                .eq("hide",false)
                .exe();
        if(Objects.isEmpty(allConfigs)){
            throw new RuntimeException("查询失败！欠费配置未找到(也可能是欠费配置状态被关闭了).");
        }
        //3、将没有配置欠费的小区设置成兜底配置
        ChargeArrearsConfig global = allConfigs.stream().filter(a -> a.getCommunityNo().equals(CommonConstant.DEFAULT_COMMUNITY_NO)).findFirst().orElse(null);
        ArrearsSetTypeEnum globalTypeEnum = global.getArrearsSetTypeEnum();

        List<ChargeArrearsConfig> match = allConfigs.stream().filter(a -> communityNos.contains(a.getCommunityNo())).collect(Collectors.toList());
        List<String> matchNos = match.stream().map(m -> m.getCommunityNo()).collect(Collectors.toList());

        List<String> noMatchNos = communityNos.stream().filter(c -> !matchNos.contains(c)).collect(Collectors.toList());
        for (String matchNo : noMatchNos) {
            ChargeArrearsConfig config = Pool.newInstance(ChargeArrearsConfig.class);
            config.setCommunityNo(matchNo);
            config.setArrearsSetTypeEnum(globalTypeEnum);
            match.add(config);
        }
        //4、区分并获取出两种配置类型的小区ID集合
        List<String> thisMonthNos = match.stream().filter(m ->
                ArrearsSetTypeEnum.THIS_MONTH.equals(m.getArrearsSetTypeEnum())).map(c -> c.getCommunityNo()).collect(Collectors.toList());
        List<String> allMonthNos = match.stream().filter(m ->
                ArrearsSetTypeEnum.PAST_AND_PRESENT.equals(m.getArrearsSetTypeEnum())).map(c -> c.getCommunityNo()).collect(Collectors.toList());

        //5、分别查询两种类型
        String yearMonthNow = new SimpleDateFormat("yyyy-MM").format(Calendar.getInstance().getTime());
        PageCondition thisMonthQuery = Condition.createPage(false);
        thisMonthQuery.eq("deleted", false);
        thisMonthQuery.in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
        thisMonthQuery.eq("billYearMonth", yearMonthNow);
        thisMonthQuery.in("communityNo",thisMonthNos);
        thisMonthQuery.groupBy("communityNo");
        thisMonthQuery.page(jsonObject);
        List<ChargeBillItemsVO> thisMonthArrears = chargeArrearsDao.statistPage(thisMonthQuery).getList();

        PageCondition allMonthQuery = Condition.createPage(false);
        allMonthQuery.eq("deleted", false);
        allMonthQuery.in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
        allMonthQuery.in("communityNo",allMonthNos);
        allMonthQuery.groupBy("communityNo");
        allMonthQuery.page(jsonObject);
        List<ChargeBillItemsVO> allMonthArrears = chargeArrearsDao.statistPage(allMonthQuery).getList();
        thisMonthArrears.addAll(allMonthArrears);

        Set<String> houseNos = thisMonthArrears.stream().map(t -> t.getHouseNo()).collect(Collectors.toSet());
        try {
            JSONObject json = new JSONObject();
            json.put("ids",houseNos);
            List<Map<String, Object>> houseList = commonClient.getHouseByIds(json);
            if(!Objects.isEmpty(houseList)){
                thisMonthArrears.stream().map(t->houseList.stream().filter(h->t.getHouseNo().equals(h.get("id"))).findFirst().map(h->{
                    String address = ConvertUtil.jointAddress(h.get("communityName").toString(), h.get("buildName").toString(),
                            h.get("unitName").toString(), h.get("mergeName").toString());
                    t.setChargeName((String)h.get("ownerName"));
                    t.setBillAddress(address);
                    return t;
                }).orElse(null)).filter(Objects::nonNull).collect(Collectors.toList());
            }
        } catch (Exception e) {
            logger.warn("欠费功能获取资产房屋错误,houseNos:{},错误信息(不影响功能):",houseNos,e);
        }
        Page<ChargeBillItemsVO> page = PageUtils.buildPage(jsonObject, thisMonthArrears);
        page.setCount(count);
        return page;
    }


    @Override
    public void statistPageExport(HttpServletRequest req, HttpServletResponse resp, JSONObject jsonObject) {
        jsonObject.put("pageNo", 1);
        jsonObject.put("pageSize", Short.MAX_VALUE);
        Page<ChargeStatistDTO> page = (Page<ChargeStatistDTO>)statistPage(jsonObject);
        List<ChargeStatistDTO> list = page.getList();
        Assert.notEmpty(list,"导出失败！没有找到相关的欠费统计数据.");
        //sql返回的是map，此处必须转换一下相同类型实体
        List<ChargeStatistDTO> dtoList = ListUtils.copyBeanList(list, ChargeStatistDTO::new);
        excelTool.excelDownload(req,resp,dtoList,"欠费统计列表导出");
    }


    @Override
    public Object findDetailPage(JSONObject jsonObject){
        String startTime = jsonObject.getString("startTime");
        String endTime = jsonObject.getString("endTime");
        String communityNo = jsonObject.getString("communityNo");



        FindPage<ChargeBillItems> find = jdbcSession.findPage(ChargeBillItems.class);
        find.eq("deleted", false);
        find.eq(jsonObject, "houseNo");

        if(!Objects.isEmpty(startTime) && !Objects.isEmpty(endTime)){
            find.maxEq("billYearMonth", startTime);
            find.minEq("billYearMonth", endTime);
        }else {
            ArrearsSetTypeEnum arrearsSetTypeEnum = this.getArrearsSetTypeEnum(communityNo);
            if(ArrearsSetTypeEnum.THIS_MONTH.equals(arrearsSetTypeEnum)){
                String yearMonthNow = new SimpleDateFormat("yyyy-MM").format(Calendar.getInstance().getTime());
                find.eq("billYearMonth", yearMonthNow);
            }
        }
        find.in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
        find.order("createTime", "desc");
        Page<ChargeBillItems> billItemsPage = find.page(jsonObject).exe();
        List<ChargeBillItems> items = billItemsPage.getList();
        items.forEach(i->{
            i.setPayableAmount(i.getPayableAmount()==null? BigDecimal.ZERO:i.getPayableAmount());
            i.setNegotiateAmount(i.getNegotiateAmount()==null?BigDecimal.ZERO:i.getNegotiateAmount());
        });
        billItemsPage.setList(items);
        return billItemsPage;
    }


    private ArrearsSetTypeEnum getArrearsSetTypeEnum(String communityNo) {
        List<ChargeArrearsConfig> configList = jdbcSession.findArray(ChargeArrearsConfig.class)
                .eq("deleted", false)
                .eq("hide", false)
                .in("communityNo", communityNo, CommonConstant.DEFAULT_COMMUNITY_NO)
                .exe();

        ChargeArrearsConfig thisCommunity = configList.stream().filter(c -> c.getCommunityNo().equals(communityNo)).findFirst().orElse(null);
        if (null == thisCommunity) {
            thisCommunity = configList.stream().filter(c -> c.getCommunityNo().equals(CommonConstant.DEFAULT_COMMUNITY_NO)).findFirst().orElse(null);
            if(null == thisCommunity){
                throw new RuntimeException("欠费配置未找到(也可能是欠费配置状态被关闭了).");
            }
        }
        return thisCommunity.getArrearsSetTypeEnum();
    }


    @Override
    public FeeMsgDetailVO getFeeMsgDetail(JSONObject jsonObject) throws Exception {
        String communityNo = jsonObject.getString("communityNo");
        String houseNo = jsonObject.getString("houseNo");
        String userNo = jsonObject.getString("userNo");
        String startMonth = jsonObject.getString("startMonth");
        String endMonth = jsonObject.getString("endMonth");

        if(Objects.isEmpty(houseNo)){
            throw new RuntimeException("houseNo不能为空.");
        }

        FindArray<ChargeBillItems> find = jdbcSession.findArray(ChargeBillItems.class);
        find.eq("deleted", false);
        find.in("billStatus", ChargeBillStatusEnum.BILL_WAIT_PAID.getId());
        find.eq("houseNo", houseNo);
        if(!Objects.isEmpty(communityNo)){
            find.eq("communityNo", communityNo);
        }
        if(!Objects.isEmpty(userNo)){
            find.eq("userNo", userNo);
        }
        if(!Objects.isEmpty(startMonth) && !Objects.isEmpty(endMonth)) {
            find.maxEq("billYearMonth", startMonth);
            find.minEq("billYearMonth", endMonth);
        } else if (!Objects.isEmpty(startMonth)) {
            find.eq("billYearMonth", startMonth);
        } else if (!Objects.isEmpty(endMonth)){
            find.min("billYearMonth", endMonth);
        }

        List<ChargeBillItems> billItemsList = find.exe();
        if(Objects.isEmpty(billItemsList)){
            return null;
        }

        String currentMonth = DateUtil.getCurrentDay(DateUtil.YEAR_MONTH_DAY);
        List<ChargeBillItems> currentMonthBills = billItemsList.stream()
                .filter(b -> b.getBillYearMonth().equals(currentMonth)).collect(Collectors.toList());
        BigDecimal currentAmountTotal = BigDecimal.ZERO;
        if (Objects.nonEmpty(currentMonthBills)) {
            currentAmountTotal = currentMonthBills.stream().map(ChargeBillItems::getPayableAmount).reduce(BigDecimal::add).get();
        }

        List<ChargeBillItems> historyMonthBills = billItemsList.stream()
                .filter(b -> !b.getBillYearMonth().equals(currentMonth)).collect(Collectors.toList());
        BigDecimal historyAmountTotal = BigDecimal.ZERO;
        if (Objects.nonEmpty(historyMonthBills)) {
            historyAmountTotal = historyMonthBills.stream().map(ChargeBillItems::getPayableAmount).reduce(BigDecimal::add).get();
        }

        List<String> billIdList = billItemsList.stream().map(ChargeBillItems::getItemBillNo).collect(Collectors.toList());
        BigDecimal allAmountTotal = BigDecimalUtils.add(currentAmountTotal, historyAmountTotal);

        JSONObject houseParam = new JSONObject();
        houseParam.put("ids",new ArrayList<>(Arrays.asList(houseNo)));
        List<Map<String, Object>> house = commonClient.getHouseByIds(houseParam);
        if(Objects.isEmpty(house)){
            return null;
        }
        Map<String, Object> houseMap = house.get(0);
        String ownerNo = ConvertUtils.toNoNullString(houseMap.get("ownerNo"));
        String ownerName = ConvertUtils.toNoNullString(jsonObject.getString("ownerName"));
        String ownerMobile = ConvertUtils.toNoNullString(houseMap.get("ownerMobile"));
        String dbCommunityNo = ConvertUtils.toNoNullString(houseMap.get("communityNo"));
        String communityName = ConvertUtils.toNoNullString(houseMap.get("communityName"));
        String buildName = ConvertUtils.toNoNullString(houseMap.get("buildName"));
        String unitName = ConvertUtils.toNoNullString(houseMap.get("unitName"));
        String mergeName = ConvertUtils.toNoNullString(houseMap.get("mergeName"));

        String address = ConvertUtil.jointAddress("", buildName, unitName, mergeName);

        FeeMsgDetailVO vo = new FeeMsgDetailVO();
        vo.setCurrentMonth(currentMonth);
        vo.setCurrentAmountTotal(currentAmountTotal);
        vo.setHistoryAmountTotal(historyAmountTotal);
        vo.setAllAmountTotal(allAmountTotal);
        vo.setOwnerNo(ownerNo);
        vo.setOwnerName(ownerName);
        vo.setOwnerMobile(ownerMobile);
        vo.setBillList(billIdList);
        vo.setDbCommunityNo(dbCommunityNo);
        vo.setCommunityName(communityName);
        vo.setAddress(address);

        StringBuilder typeName = new StringBuilder();
        for (int i = 0; i < billItemsList.size(); i++) {
            ChargeBillItems temp = billItemsList.get(i);
            typeName.append(temp.getFeeTypeName()).append("、");
        }
        typeName.deleteCharAt(typeName.length() -1);
        vo.setFeeTypeName(typeName.toString());
        return vo;
    }


    @Override
    public void batchUrgingByResidentIds(JSONObject jsonObject, Token token) throws Exception {

        JSONArray residentIds = jsonObject.getJSONArray("residentIds");
        Assert.notEmpty(residentIds,"催费失败！请选择需要催费的住户");
        //获取管家信息
        JSONObject externalInfo = commonClient.getExternalId(token.getUuid());
        if(null == externalInfo){
            logger.error("[批量催费]获取管家(token):{} 的企微外部ID失败。",token);
            throw new RuntimeException("催费失败！无法获取管家的企微信息.");
        }
        String userId = externalInfo.getString("userId");
        //获取住户外部信息
        List<ArrearsBatchUrgingVO> arrearsInfo = commonClient.batchResidentExternalInfo(residentIds);
        if(Objects.isEmpty(arrearsInfo)){
            logger.error("[批量催费]获取住户:{}外部ID失败。",residentIds);
            throw new RuntimeException("催费失败！获取住户外部ID失败，请使用单独催费功能.");
        }
        Set<String> externalIds = arrearsInfo.stream().map(a -> a.getExternalId()).collect(Collectors.toSet());
        //消息模板（获取小程序APPID和跳转路径）
        String templateTypeName = TemplateType.BATCH_PAY_CALL.getId();
        JSONObject templateType = new JSONObject();
        templateType.put("templateType",templateTypeName);
        JSONObject smgTemplate = commonClient.getTemplateByType(templateType);
        if(null == smgTemplate){
            logger.error("[批量催费]获取模板类型:{}失败。",templateTypeName);
            throw new RuntimeException("催费失败！消息模板未初始化，请通知系统管理员.");
        }
        //处理 media_id
        List<ChargeUrgingCover> cover = jdbcSession.findArray(ChargeUrgingCover.class)
                .eq("deleted", false)
                .exe();
        ChargeUrgingCover urgingCover;
        if(Objects.isEmpty(cover)){
            urgingCover = this.initCoverImages();
        }else {
            urgingCover = cover.get(0);
        }
        String imageId = urgingCover.getImageId();
        if(Objects.isEmpty(imageId)){
            logger.error("[批量催费]获取封面图片失败.");
            throw new RuntimeException("消息封面图片初始化失败，请通知系统管理员.");
        }
        String mediaId = urgingCover.getMediaId();
        //计算 media_id 时间差（三天过期）
        Timestamp refreshSrt = urgingCover.getRefresh();
        if (null == refreshSrt) {
            refreshSrt = new Timestamp(DateUtil.addDays(new Date(),-3L).getTime());
        }
        long lastTime = DateUtil.compareCurrentTime(refreshSrt);
        if(MEDIA_ID_EXPIRES<lastTime){
            JSONObject refreshMedia = commonClient.refreshMediaId(token, imageId);
            String expiredTime = refreshMedia.getString("expiredTime");
            mediaId = refreshMedia.getString("media_id");
            ChargeUrgingCover dbCover = jdbcSession.findOne(ChargeUrgingCover.class)
                    .eq("deleted", false)
                    .eq("imageId", imageId)
                    .exe();

            dbCover.setMediaId(mediaId);
            dbCover.setRefresh(Timestamp.valueOf(expiredTime));
            dbCover.setUpdateTime(System.currentTimeMillis());
            jdbcSession.updateById(dbCover);
        }

        JSONObject json = new JSONObject();
        json.put("companyNo",token.getCompanyNo());
        json.put("externalIds",externalIds);
        json.put("userId",userId);
        json.put("text",String.format("您%s月的账单已出，请注意查收~",DateUtil.getMonth()));
        json.put("title","美好小区");
        json.put("appid",smgTemplate.getString("miniAppId"));
        json.put("mediaId",mediaId);
        json.put("miniPage",smgTemplate.getString("miniPagePath"));
        commonClient.sendEntWechatMsg2MiniProgram(json);
    }
}
